// 淘宝开放平台SDK基础
package base

import (
	"bytes"
	"crypto/md5"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	apiVersion = "2.0"                 // API协议版本
	signMethod = "md5"                 // 签名的摘要算法
	bodyFormat = "json"                // 响应格式
	timeFormat = "2006-01-02 15:04:05" // 时间戳格式

	apiAddrPE = "https://eco.taobao.com/router/rest"       // 正式环境请求地址
	apiAddrSE = "https://gw.api.tbsandbox.com/router/rest" // 沙箱环境请求地址
)

type Client struct {
	AppKey       string // TOP分配给应用的AppKey
	AppSecret    string // TOP分配给应用的AppSecret
	Session      string // TOP颁发给应用的授权信息
	PartnerID    string // 合作伙伴身份标识
	TargetAppKey string // 被调用的目标AppKey
	Simplify     bool   // 是否采用精简JSON返回格式
	SandEnv      bool   // 是否采用沙箱环境
}

func NewClient(appKey, appSecret string) *Client {
	return &Client{
		AppKey:    appKey,
		AppSecret: appSecret,
	}
}

func (c *Client) PostForm(method string, params url.Values) (*Response, error) {
	// 公共参数
	params.Set("method", method) // API接口名称
	params.Set("app_key", c.AppKey)
	params.Set("timestamp", time.Now().Format(timeFormat))
	params.Set("format", bodyFormat)
	params.Set("v", apiVersion)

	if len(c.Session) > 0 {
		params.Set("session", c.Session)
	}
	if len(c.PartnerID) > 0 {
		params.Set("partner_id", c.PartnerID)
	}
	if len(c.TargetAppKey) > 0 {
		params.Set("target_app_key", c.TargetAppKey)
	}
	if c.Simplify != false {
		params.Set("simplify", strconv.FormatBool(c.Simplify))
	}

	params.Set("sign_method", signMethod)
	params.Set("sign", c.genSign(params)) // API输入参数签名结果

	var url string
	if c.SandEnv {
		url = apiAddrSE
	} else {
		url = apiAddrPE
	}
	resp, err := http.PostForm(url, params)
	if err != nil {
		return nil, err
	}
	return NewResponse(resp)
}

// API输入参数签名算法
func (c *Client) genSign(params url.Values) string {
	keys := make([]string, 0, len(params))
	for k, _ := range params {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	var buf bytes.Buffer
	buf.WriteString(c.AppSecret)
	for _, k := range keys {
		buf.WriteString(k)
		buf.WriteString(params.Get(k))
	}
	buf.WriteString(c.AppSecret)

	res := fmt.Sprintf("%x", md5.Sum(buf.Bytes()))
	return strings.ToUpper(res)
}
